// 实现React的可中断渲染
function Counter() {
  return {
    // <div>
    //   <span>helloWorld</span>
    //   <button>click me</button>
    // </div>
    // React.createElement('div', {}, React.createElement('span', {}, 'helloWorld'),
    //   React.createElement('button', {}, 'click me'))
    // children: {
    type: "span",
    value: "helloWorld", // fiber里还要区分文本节点和react元素节点
    next: {
      type: "button",
      value: "click me"
    }
    // }
  }
}

// createElement转换成什么样子 concurrency 实际上是对createElement的执行结果进行可中断化

// 链表 ---》 useState hooks源码 都要用链表 hooks的核心机制就是环形链表
const CounterElementDescriptors = {
  type: "Function",
  fn: Counter
}


// 我们要做的就是如何将这堆数据进行可中断化
let presentWork = null; // 目前要做的工作
let rootElementDescriptor = null
let elementsContainer = null

function performUnitOfWork(deadline) {
  // 我们需要判定一下根组件
  // 代表目前没有工作要做了 双等会强制类型转换 undefined == null
  if (presentWork == null) { // 第二次进来是span
    console.log("presentWork", rootElementDescriptor)
    commitRoot(rootElementDescriptor)
    return;
  }
  // 当前有工作我们还要看一个东西 这个东西不确定
  if (deadline.timeRemaining() < 1) { // 当前帧是否还有有额外的时间 判断deadline的didTimeout timeRemaining --->
    // 会返回当前帧剩余的时间， 单位是ms

    // 假设到span时 当前帧已经没有空闲时间了
    // 我们把任务推进下一帧执行
    // 这样做的话 我们是不是应该把本来应该在一帧内执行完的渲染任务分到多帧去拉
    // 假设当前帧没有时间了 ---》 渲染工作停了 好像中断了
    requestIdleCallback(executeWorkLoop) // 把span推向下一帧
    // 如果span执行完以后 我们又要渲染button 如果span真的要渲染16ms 那么我们又将button推进下一帧
    // 这样我们是不是保证了 无论你的组件写的有多大 他实际上都会被拆分成很多小的任务去分散在每一帧里执行
    // 那这样的话就不会出现掉帧 用户的交互是不是不会失效
    return;
  }
  if (presentWork.type === "Function") {
    // 一个函数组件再怎么写也不可能超过16ms
    // 我们之前for 500000
    // 常规的百分之九十九点九的函数执行事件是不能超过16ms的 剩下的百分之一你得看看是不是自己写的问题
    // 多个就有可能
    rootElementDescriptor = presentWork // 保存下跟引用
    // 代表是组件
    const fstChildren = presentWork.fn();// 执行非常多的逻辑 //很多个组件的非常多的逻辑加在一起是不是就可能超过16ms
    console.log("children", fstChildren)
    fstChildren.parent = presentWork; // 这个跟我们这节课么关系，主要根fiber有关系
    presentWork.children = fstChildren;
    // 开始下一阶段工作
    presentWork = fstChildren; // 等于把当前工作的元素从函数的组件变成了第一个span元素
    performUnitOfWork(deadline);

    // 渲染完span 是不是又要渲染button
    // 假设渲染span就要耗费16ms
    //
  } else {
    // 带表的是真实元素
    const dom = document.createElement(presentWork.type);
    dom.innerHTML = presentWork.value
    // 不会马上塞到页面中去 render阶段
    presentWork.dom = dom
    // 比方说你有class 事件 事件也要绑定上
    // 等待commit 一次性提交到页面去
    presentWork = presentWork.next; // 转到button // button.next 没东西了
    performUnitOfWork(deadline);// 是不是还是当前的deadline 那就意味着多次递归用的都是同一个deadline的对象didTimeout ---> 布尔值他是不会变化的
  }
}

// 你把fiber直接挂倒页面 生成真实dom那个我说了逻辑很简单 所以不可能卡帧
// 执行我们自己的工作的时候 去将一个大的任务拆分成多个任务去执行 让每一帧的最大可渲染单元为组件 【当然如果本帧时间充裕的情况下可以渲染多个组件
// ，但是只要发现不充裕立马推入下一帧】如果说有连续两帧都没时间 是不是渲染就被推后了两帧 是不是有意味着停止了两帧没有进行渲染工作，停止是不是中断
//所以这是不是可中断渲染
// 】
function executeWorkLoop(deadline) {
  console.log("executeWorkLoop----", deadline)
  // deadline.didTimeout: 表示当前帧是否有空闲时间
  performUnitOfWork(deadline)
}


// 做render阶段
function render(element) {
  console.log("ele", element)
  elementsContainer = element;
  presentWork = CounterElementDescriptors;
  requestIdleCallback(executeWorkLoop);
}

// 做commit阶段
function commitRoot(_rootElement) {
  // 进入commit阶段
  console.log("进入commit阶段，开始生成真实dom", _rootElement)
  let renderChildrenElements = _rootElement.children;
  do {
    console.log("come in", renderChildrenElements, elementsContainer)
    elementsContainer.appendChild(renderChildrenElements.dom);
    renderChildrenElements = renderChildrenElements.next;
  } while (renderChildrenElements)
}

render(document.getElementById("root"))

// requestIdleCallback() --> 你给他传一个回调 他会在下一帧还有时间的时候去执行这个回调, 如果下一帧没有足够的事件
// 则不会执行你的回调